//////////////////////////////////////////////////////////////////////////////

//  Task queue thread

//////////////////////////////////////////////////////////////////////////////

#ifndef	__MAGUS_TASK_LOOP_HPP
#define	__MAGUS_TASK_LOOP_HPP

//////////////////////////////////////////////////////////////////////////////

#include <queue>
#include <cassert>

#include <magus/config.hpp>			// configuration detection

#ifdef __MAGUS_POSIX				// POSIX thread model
#	include <pthread.h>
#	include <unistd.h>

#elif defined __MAGUS_WIN32			// Win32 thread model
#	include <windows.h>
#	include <process.h>
#endif

#include <magus/thread_sync.hpp>	// for thread synchronization facility
#include <boost/utility.hpp>		// for utility "noncopyable"

//////////////////////////////////////////////////////////////////////////////

namespace magus
{

	template <typename taskT, typename handlerT>
	class task_loop : private boost::noncopyable
	{
	public:

		// Constructor & destructor
		task_loop() : _M_running(false), _M_p_handler(new handlerT) {}
		explicit task_loop(handlerT* p_handler)
			: _M_running(false), _M_p_handler(p_handler)
		{
			assert(p_handler!=NULL);
		}
		~task_loop()
		{
			shutdown();
			delete _M_p_handler;
		}

		// startup task loop thread
		bool startup()
		{
			magus::mutex::scoped_lock lock(_M_running_mtx);

			// Thread already running
			if( _M_running )
				return true;

			// Create thread
			if( !create_thread() )
				return false;

			// Wait until thread running
			while( !_M_running )
				sleep_1s();

			return true;
		}

		// shutdown task loop thread
		void shutdown()
		{
			magus::mutex::scoped_lock lock(_M_running_mtx);

			// Thread already exited
			if( !_M_running )
				return;

			// Set exit flag
			_M_exiting = true;

			// Fake signal to wakeup thread
			_M_not_empty.notify_one();

			// Wait until thread exited
			while( _M_running )
				sleep_1s();
		}

		// is thread running ?
		bool is_running(){ return _M_running; }

		// add a task to queue
		void add_task(const taskT& t)
		{
			mutex::scoped_lock lock(_M_mtx);
			_M_queue.push(t);
			_M_not_empty.notify_one();
		}

		// Get the handler of this task loop, so client can change its state
		handlerT& get_handler(){ assert(_M_p_handler!=NULL); return *_M_p_handler; }

	private:

		bool _M_running;				// thread running flag
		bool _M_exiting;				// thread exiting flag
		magus::mutex _M_running_mtx;	// startup & shutdown mutex

		handlerT* _M_p_handler;			// pointer to task handler

		std::queue<taskT> _M_queue;		// task queue
		magus::mutex _M_mtx;			// access task queue mutex
		magus::condition _M_not_empty;	// task queue not empty condition

#ifdef __MAGUS_POSIX					// POSIX thread model

		// thread id
		pthread_t _M_tid;

		// thread rountine
		static void* thread_func(void* p)
		{
			task_loop* p_obj = static_cast<task_loop*>(p);
			p_obj->loop_func();
			return NULL;
		}

		// create thread
		bool create_thread()
		{
			return ( pthread_create(&_M_tid, NULL, thread_func, this) == 0 );
		}

		// sleep 1 second
		void sleep_1s(){ sleep(1); }

#elif defined __MAGUS_WIN32			// Win32 thread model

		// thread rountine
		static void thread_func(void* p)
		{
			task_loop* p_obj = static_cast<task_loop*>(p);
			p_obj->loop_func();
		}

		// create thread
		bool create_thread()
		{
			return ( _beginthread(thread_func, 0, this) != -1 );
		}

		// sleep 1 second
		void sleep_1s(){ Sleep(1000); }

#endif

		inline void loop_func()
		{
			// thread is running now
			_M_running = true;

			taskT task_;
			while(true)
			{
				{
					mutex::scoped_lock lock(_M_mtx);

					// Wait until queue isn't empty
					if( _M_queue.empty() )
						_M_not_empty.wait(lock);

					// Should I exit?
					if( _M_exiting )
						break;

					// Get task
					task_ = _M_queue.front();
					_M_queue.pop();
				}

				// call task handler
				(*_M_p_handler)(task_);
			}

			// cleanup tasks in queue
			while(!_M_queue.empty())
			{
				task_ = _M_queue.front();
				_M_queue.pop();
				(*_M_p_handler)(task_);
			}

			// Thread isn't running now
			_M_running = false;
		}

	};		// 	class task_loop

}	// namespace magus

//////////////////////////////////////////////////////////////////////////////

#endif		// #ifndef	__MAGUS_TASK_LOOP_HPP
